# 与 Android(adb) 集成

Midscene 支持使用视觉语言（VL）模型来控制 Android 设备。

import { PackageManagerTabs } from '@theme';

:::info 样例项目
你可以在这里看到向 Android 集成的样例项目：[https://github.com/web-infra-dev/midscene-example/blob/main/android-demo](https://github.com/web-infra-dev/midscene-example/blob/main/android-demo)

这里还有一个 Android 和 Vitest 结合的样例项目：[https://github.com/web-infra-dev/midscene-example/tree/main/android-with-vitest-demo](https://github.com/web-infra-dev/midscene-example/tree/main/android-with-vitest-demo)
:::

## 准备工作

### 配置 API Key

配置 VL 模型的 API Key。如果使用 qwen-2.5-vl，可以配置如下：

```bash
OPENAI_BASE_URL="https://dashscope.aliyuncs.com/compatible-mode/v1"
OPENAI_API_KEY="......"
MIDSCENE_MODEL_NAME="qwen-vl-max-latest"
MIDSCENE_USE_QWEN_VL=1
```

控制 Android 设备时，你只能使用 VL 模型。更多详情请参考 [选择模型](./choose-a-model)。

### 准备 adb 环境

`adb` 是一个命令行工具，允许你与 Android 设备通信。

- 方式一：使用 [Android Studio](https://developer.android.com/studio?hl=zh-cn) 安装
- 方式二：使用 [Android command-line tools](https://developer.android.com/studio#command-line-tools-only) 安装

验证 adb 是否安装成功：

```bash
adb --version
```

当你看到如下输出，说明 adb 已经安装成功：

```log
Android Debug Bridge version 1.0.41
Version 34.0.4-10411341
Installed as /usr/local/bin//adb
Running on Darwin 24.3.0 (arm64)
```

### 使用 adb 连接 Android 设备

在系统设置的开发者选项中，开启安卓设备的『USB 调试』，如果存在『USB 调试（安全设置）』，也需要开启，接着使用数据线连接一台安卓设备

<p align="center">
  <img src="/android-usb-debug.png" alt="android usb debug" width="400"/>
</p>

通过下面命令确认已经连接：

```bash
adb devices -l
```

当你看到如下输出，说明连接成功：

```log
List of devices attached
s4ey59	device usb:34603008X product:cezanne model:M2006J device:cezan transport_id:3
```

## 第一步：安装依赖

<PackageManagerTabs command="install @midscene/android --save-dev" />

## 第二步：编写脚本

这里以使用安卓浏览器搜索耳机为例。(当然，你也可以使用设备上的其他任何应用)

编写下方代码，保存为 `./demo.ts`

```typescript title="./demo.ts"
import { AndroidAgent, AndroidDevice, getConnectedDevices } from '@midscene/android';

const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
Promise.resolve(
  (async () => {
    const devices = await getConnectedDevices();
    const page = new AndroidDevice(devices[0].udid);

    // 👀 初始化 Midscene agent
    const agent = new AndroidAgent(page,{
      aiActionContext:
        '如果出现位置、权限、用户协议等弹窗，点击同意。如果出现登录页面，关闭它。',
    });
    await page.connect();

    // 👀 打开浏览器并导航到 ebay.com（请确保当前页面有浏览器 App 喔）
    await agent.aiAction('open browser and navigate to ebay.com');

    await sleep(5000);

    // 👀 输入关键词，执行搜索
    await agent.aiAction('在搜索框输入 "Headphones" ，敲回车');

    // 👀 等待加载完成
    await agent.aiWaitFor("页面中至少有一个耳机商品");
    // 或者你也可以使用一个普通的 sleep:
    // await sleep(5000);

    // 👀 理解页面内容，提取数据
    const items = await agent.aiQuery(
      "{itemTitle: string, price: Number}[], 找到列表里的商品标题和价格"
    );
    console.log("耳机商品信息", items);

    // 👀 用 AI 断言
    await agent.aiAssert("界面左侧有类目筛选功能");
  })()
);

```

## 第三步：运行

使用 `tsx` 来运行

```bash
# run
npx tsx demo.ts
```

稍等片刻，你会看到如下输出：

```log
[
 {
   itemTitle: 'JBL Tour Pro 2 - True wireless Noise Cancelling earbuds with Smart Charging Case',
   price: 551.21
 },
 {
   itemTitle: 'Soundcore Space One无线耳机40H ANC播放时间2XStronger语音还原',
   price: 543.94
 }
]
```

## 第四步：查看运行报告

当上面的命令执行成功后，会在控制台输出：`Midscene - report file updated: /path/to/report/some_id.html`， 通过浏览器打开该文件即可看到报告。

## Android Agent 上的更多接口

除了 [API 参考](./API) 中的通用 Agent 接口，AndroidAgent 还提供了一些其他接口：

### `agent.launch()`

启动一个网页或原生页面。

* 类型

```typescript
function launch(uri: string): Promise<void>;
```

* 参数：
  * `uri: string` - 要打开的 uri

* 返回值：
  * `Promise<void>`

* 示例：

```typescript
import { AndroidAgent, AndroidDevice } from '@midscene/android';

const page = new AndroidDevice('s4ey59ytbitot4yp');
const agent = new AndroidAgent(page);

await agent.launch('https://www.ebay.com'); // 打开网页
await agent.launch('com.android.settings'); // 打开系统设置 app(package name)
await agent.launch('com.android.settings/.Settings'); // 打开系统设置 app(package name) 的 .Settings(activity name) 页面
```

### `agentFromAdbDevice()`

从已连接的 adb 设备中，创建一个 AndroidAgent。

* 类型

```typescript
function agentFromAdbDevice(deviceId?: string, opts?: PageAgentOpt): Promise<AndroidAgent>;
```

* 参数：
  * `deviceId?: string` - 可选参数，要连接的 adb 设备 id，如果未传入，则使用第一个连接的设备
  * `opts?: PageAgentOpt` - 可选参数，用于初始化 AndroidAgent 的配置，参考 [构造器](./API)

* 返回值：
  * `Promise<AndroidAgent>` 返回一个 AndroidAgent 实例

* 示例：

```typescript
import { agentFromAdbDevice } from '@midscene/android';

const agent = await agentFromAdbDevice('s4ey59ytbitot4yp'); // 传入 deviceId
const agent = await agentFromAdbDevice(); // 不传入 deviceId，则使用第一个连接的设备
```

### `getConnectedDevices()`

获取所有连接的 Android 设备。

* 类型

```typescript 
function getConnectedDevices(): Promise<Device[]>;
interface Device {
  /**
   * The device udid.
   */
  udid: string;
  /**
   * Current device state, as it is visible in
   * _adb devices -l_ output.
   */
  state: string;
  port?: number;
}
```

* 返回值：
  * `Promise<Device[]>` 返回一个 Device 数组

* 示例：

```typescript
import { agentFromAdbDevice, getConnectedDevices } from '@midscene/android'

const devices = await getConnectedDevices();
console.log(devices);
const agent = await agentFromAdbDevice(devices[0].udid);
```

## 更多

* 更多 Agent 上的 API 接口请参考 [API 参考](./API)。
* 更多关于提示词的技巧请参考 [提示词技巧](./prompting-tips)

## FAQ

### 为什么我连接了设备，但是通过 adb 仍然无法控制？

请检查是否在系统设置的开发者选项中，如果存在『USB 调试（安全设置）』，也需要开启。

<p align="center">
  <img src="/android-usb-debug.png" alt="android usb debug" width="400"/>
</p>

### 为什么我连接了设备，但是通过 `adb devices -l` 命令看不到设备？

[连接设备](./integrate-with-android#使用-adb-连接-android-设备) 时，请确保设备处于解锁状态。